1
|
|
|
import React from 'react'; |
2
|
|
|
import PropTypes from 'prop-types'; |
3
|
|
|
import ReactDOM from 'react-dom'; |
4
|
|
|
|
5
|
|
|
import { |
6
|
|
|
Row, |
7
|
|
|
Col, |
8
|
|
|
FormControl, |
9
|
|
|
ButtonToolbar, |
10
|
|
|
ButtonGroup, |
11
|
|
|
Button, |
12
|
|
|
OverlayTrigger, |
13
|
|
|
Tooltip, |
14
|
|
|
} from 'react-bootstrap'; |
15
|
|
|
|
16
|
|
|
const defaultCondition = { comparator: '*' }; |
17
|
|
|
const defaultConditionAdded = { comparator: '~' }; |
18
|
|
|
|
19
|
|
|
export default class VersionSelector extends React.Component { |
20
|
|
|
constructor(props) { |
21
|
|
|
super(props); |
22
|
|
|
this.state = { |
23
|
|
|
values: [defaultCondition], |
24
|
|
|
addButtonDisabled: false, |
25
|
|
|
}; |
26
|
|
|
this.focusInputs = {}; |
27
|
|
|
} |
28
|
|
|
|
29
|
|
|
componentWillReceiveProps(newProps) { |
30
|
|
|
if (newProps.values) { |
31
|
|
|
this.setState({ |
32
|
|
|
values: newProps.values, |
33
|
|
|
addButtonDisabled: newProps.values.some(val => val.comparator === '*'), |
34
|
|
|
}); |
35
|
|
|
} |
36
|
|
|
} |
37
|
|
|
|
38
|
|
|
onVersionChanged(index, changedItem) { |
39
|
|
|
const newValues = this.state.values.slice(); |
40
|
|
|
newValues[index] = Object.assign({}, this.state.values[index], changedItem); |
41
|
|
|
this.setState({ |
42
|
|
|
values: newValues, |
43
|
|
|
addButtonDisabled: newValues.some(val => val.comparator === '*'), |
44
|
|
|
}, () => { |
45
|
|
|
if (changedItem.comparator && this.focusInputs[index][changedItem.comparator]) { |
46
|
|
|
ReactDOM.findDOMNode(this.focusInputs[index][changedItem.comparator]).focus(); |
47
|
|
|
} |
48
|
|
|
this.onChanged(); |
49
|
|
|
}); |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
onChanged() { |
53
|
|
|
if (typeof this.props.onChange === 'function') { |
54
|
|
|
this.props.onChange(this.state.values); |
55
|
|
|
} |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
addCondition() { |
59
|
|
|
const newValues = this.state.values.slice(); |
60
|
|
|
newValues.push(defaultConditionAdded); |
61
|
|
|
this.setState({ values: newValues }, () => this.onChanged()); |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
removeCondition(idx) { |
65
|
|
|
const newValues = this.state.values.slice(); |
66
|
|
|
newValues.splice(idx, 1); |
67
|
|
|
this.setState({ values: newValues }, () => this.onChanged()); |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
render() { |
71
|
|
|
const values = this.state.values || [defaultCondition]; // default |
72
|
|
|
return ( |
73
|
|
|
<div className="version-selector-container"> |
74
|
|
|
{values.map((value, index) => { |
75
|
|
|
return ( |
76
|
|
|
<div className="selector-item" key={index}> |
77
|
|
|
<ButtonToolbar className="selector-comparator"> |
78
|
|
|
<ButtonGroup> |
79
|
|
|
<OverlayTrigger placement="top" overlay={<Tooltip>모든 범위</Tooltip>}> |
80
|
|
|
<Button |
81
|
|
|
active={value.comparator === '*'} |
82
|
|
|
onClick={() => this.onVersionChanged(index, { comparator: '*' })} |
83
|
|
|
disabled={this.props.disabled} |
84
|
|
|
> |
85
|
|
|
✱ {/* asterisk */} |
86
|
|
|
</Button> |
87
|
|
|
</OverlayTrigger> |
88
|
|
|
<OverlayTrigger placement="top" overlay={<Tooltip>범위 지정</Tooltip>}> |
89
|
|
|
<Button |
90
|
|
|
active={value.comparator === '~'} |
91
|
|
|
onClick={() => this.onVersionChanged(index, { comparator: '~' })} |
92
|
|
|
disabled={this.props.disabled} |
93
|
|
|
> |
94
|
|
|
∼ {/* tilda */} |
95
|
|
|
</Button> |
96
|
|
|
</OverlayTrigger> |
97
|
|
|
<OverlayTrigger placement="top" overlay={<Tooltip>일치</Tooltip>}> |
98
|
|
|
<Button |
99
|
|
|
active={value.comparator === '='} |
100
|
|
|
onClick={() => this.onVersionChanged(index, { comparator: '=' })} |
101
|
|
|
disabled={this.props.disabled} style={{ paddingTop: '5px', paddingBottom: '7px' }} |
102
|
|
|
> |
103
|
|
|
= |
104
|
|
|
</Button> |
105
|
|
|
</OverlayTrigger> |
106
|
|
|
</ButtonGroup> |
107
|
|
|
</ButtonToolbar> |
108
|
|
|
<FormControl |
109
|
|
|
className="selector-inputs" |
110
|
|
|
type="text" |
111
|
|
|
value={'모든 버전 대상'} |
112
|
|
|
style={value.comparator !== '*' ? { display: 'none' } : {}} |
113
|
|
|
readOnly |
114
|
|
|
/> |
115
|
|
|
<FormControl |
116
|
|
|
ref={(f) => { |
117
|
|
|
if (!this.focusInputs[index]) { |
118
|
|
|
this.focusInputs[index] = {}; |
119
|
|
|
} |
120
|
|
|
this.focusInputs[index]['~'] = f; |
121
|
|
|
}} |
122
|
|
|
className="selector-inputs" |
123
|
|
|
type="text" |
124
|
|
|
value={value.versionStart} |
125
|
|
|
onChange={e => this.onVersionChanged(index, { versionStart: e.target.value })} |
126
|
|
|
placeholder=">= 시작 버전 (X.Y.Z 형태)" |
127
|
|
|
disabled={this.props.disabled} |
128
|
|
|
style={value.comparator !== '~' ? { display: 'none' } : {}} |
129
|
|
|
/> |
130
|
|
|
<FormControl |
131
|
|
|
className="selector-inputs selector-inputs-second" |
132
|
|
|
type="text" |
133
|
|
|
value={value.versionEnd} |
134
|
|
|
onChange={e => this.onVersionChanged(index, { versionEnd: e.target.value })} |
135
|
|
|
placeholder="< 끝 버전 (X.Y.Z 형태)" |
136
|
|
|
disabled={this.props.disabled} |
137
|
|
|
style={value.comparator !== '~' ? { display: 'none' } : {}} |
138
|
|
|
/> |
139
|
|
|
<FormControl |
140
|
|
|
ref={(f) => { |
141
|
|
|
if (!this.focusInputs[index]) { |
142
|
|
|
this.focusInputs[index] = {}; |
143
|
|
|
} |
144
|
|
|
this.focusInputs[index]['='] = f; |
145
|
|
|
}} |
146
|
|
|
className="selector-inputs" |
147
|
|
|
type="text" |
148
|
|
|
value={value.version} |
149
|
|
|
onChange={e => this.onVersionChanged(index, { version: e.target.value })} |
150
|
|
|
placeholder="= 일치하는 버전 (X.Y.Z 형태)" |
151
|
|
|
style={value.comparator !== '=' ? { display: 'none' } : {}} |
152
|
|
|
disabled={this.props.disabled} |
153
|
|
|
/> |
154
|
|
|
<Button |
155
|
|
|
className="selector-remove-btn" |
156
|
|
|
onClick={() => this.removeCondition(index)} |
157
|
|
|
disabled={index === 0 || this.props.disabled} |
158
|
|
|
> |
159
|
|
|
✕ |
160
|
|
|
</Button> |
161
|
|
|
</div> |
162
|
|
|
); |
163
|
|
|
})} |
164
|
|
|
<Row> |
165
|
|
|
<Col xs={12}> |
166
|
|
|
<Button |
167
|
|
|
className="selector-add-btn" |
168
|
|
|
onClick={() => this.addCondition()} |
169
|
|
|
disabled={this.props.disabled || this.state.addButtonDisabled} |
170
|
|
|
> |
171
|
|
|
+ 조건 추가 (OR) |
172
|
|
|
</Button> |
173
|
|
|
</Col> |
174
|
|
|
</Row> |
175
|
|
|
</div> |
176
|
|
|
); |
177
|
|
|
} |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
VersionSelector.propTypes = { |
181
|
|
|
values: PropTypes.arrayOf(PropTypes.shape({ |
182
|
|
|
comparator: PropTypes.string.isRequired, |
183
|
|
|
versionStart: PropTypes.string, |
184
|
|
|
versionEnd: PropTypes.string, |
185
|
|
|
version: PropTypes.string, |
186
|
|
|
})), |
187
|
|
|
onChange: PropTypes.func, |
188
|
|
|
disabled: PropTypes.bool, |
189
|
|
|
}; |
190
|
|
|
|